from interfaces import IDict, IList
from xmldict import ___is_dict, ___is_list
from xml.dom import Node
__AT__ = u'@'

#{{{ Read dict and attrs
def read_attrs(element, dest):
	""" Read _only_ attributes from XML element into dict
	"""
	if not element.attributes :
		return
	idict = IDict.providedBy(dest)
	if idict:
		dest.createValue(__AT__)

	if __AT__ in dest:
		at = dest[__AT__]
	else:
		dest[__AT__] = dict()
		at = dest[__AT__]

	for name, value in element.attributes.items():
		at[name] = value          # XXX modification to minidom

def read_dict(element, strip=True, force_dict=False):
	if strip: s= lambda x:x.strip()
	else: s= lambda x: x

	#{{{ Utility methods
	def getText(element):
		""" Concatenates all text and CDATA children
		"""
		text = ''
		for node in element.childNodes:
			if node.nodeType == Node.TEXT_NODE and node.data:
				text += s(node.data)
			if node.nodeType == Node.CDATA_SECTION_NODE and node.data:
				text += s(node.data)
		return text or None
	def ___childNodes(element):
		result = []
		for node in element.childNodes:
			if node.nodeType == Node.ELEMENT_NODE:
				result.append(node)
		return result

	#}}}

	dest = {}

	text = getText(element)
	read_attrs(element, dest) # put attributes in __AT__ subdict if any

	if text:
		if __AT__ in dest: # we have met attributes before
			at = dest[__AT__]
			at[__AT__] = text # put text as __AT__ attribute
		elif force_dict:   # force text to be like attributes have been met
			dest[__AT__] = {__AT__:text}
		else:
			dest[__AT__] = text # allow __AT__ to be plain

	for node in ___childNodes(element):
		key = node.nodeName

		has_children  = len(___childNodes(node)) > 0
		has_attributes = len(node.attributes) > 0
		if force_dict or has_children or has_attributes:
			dval = read_dict(node, strip, force_dict) # add as dictionary anyway
		else:
			text = getText(node)
			if text:
				dval = text
			else:
				dval = {} # force to be dictionary
		#dval is object for this subnode :)

		if key not in dest: # key first met
			dest[key] = dval
			continue
		# key have been met before. So we have to create list for same
		cur = dest[key]
		if ___is_list(cur):
			l = cur
		else:
			l = list()
			l.append(cur)
		l.append(dval)
		dest[key] = l
	return dest
#}}}
#{{{ write dict, list and attrs
def appendTextNode(document, element, text):
	text = str(text).strip()
	if text:
		tNode = document.createTextNode(text)
		element.appendChild(tNode)

def write_attrs(document, element, at):
	""" Writes attributes and text to XML
	"""
	at_str = None
	if ___is_dict(at):
		if __AT__ in at:
			at_str = at[__AT__]
		for key in at:
			if key == __AT__:
				continue
			element.setAttribute(key, str(at[key]))
	elif ___is_list(at):
		raise "Can't write attributes from list"
	elif at is not None: # read only text from this node
		at_str = at
	if at_str:
		appendTextNode(document, element, at_str)

def write_list(document, element, keyName, source):
	""" Write list of same nodes to XML. keyName is node name
	"""
	for obj in source:
		if not obj:
			pass
		if ___is_list(obj):
			raise TypeError("Don't know what to do with %s"%((type(obj),)))
		e = document.createElement(keyName)
		element.appendChild(e)
		if ___is_dict(obj):
			write_dict(document, e, obj)
		else:
			appendTextNode(document, e, obj)

def write_dict(document, element, source):
	""" Write dictionary tree to XML
	"""
	if source is None:
		raise ValueError('Given source should not be None')
	if not ___is_dict(source):
		raise TypeError('Type %s is not dict'%str(type(_dict)))

	if __AT__ in source:
		write_attrs(document, element, source[__AT__])

	for key in source.keys():
		if key == __AT__: # had been written
			continue

		data = source[key]

		if data is None:
			continue

		if ___is_dict(data):
			dElement = document.createElement(key)
			element.appendChild(dElement)
			write_dict(document, dElement, data)
		elif ___is_list(data):
			write_list(document, element, key, data)
		else:
			dElement = document.createElement(key)
			element.appendChild(dElement)
			appendTextNode(document, dElement, data)
#}}}
